#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
代码注入器
从文件读取代码片段，注入到目标代码文件中
"""

import os
import random
from pathlib import Path
from typing import List, Dict, Optional

from core.builtin_config_module.builtin_config import BuiltInConfig
from core.utils_module.language_utils import detect_language_for_file
from core.utils_module.logger import ObfuscationLogger

class CodeInjector:
    """代码注入器 - 将预定义的代码片段注入到目标文件"""

    def __init__(self, config: BuiltInConfig, logger: ObfuscationLogger):
        """
        初始化代码注入器

        Args:
            config: 统一配置对象
            logger: 日志记录器
        """
        self.config = config
        self.logger = logger
        self.injected_count = 0

    def inject_from_file(self, content_file: str,
                        target_dirs: Optional[List[str]] = None) -> bool:
        """
        从文件读取代码片段并注入

        Args:
            content_file: 代码片段文件路径
            target_dirs: 目标目录列表（如果为None，使用配置）

        Returns:
            是否成功
        """
        try:
            # 使用参数或配置
            content_file = content_file or self.config.advanced_features.injection_content_file
            target_dirs = target_dirs or self.config.advanced_features.injection_target_dirs

            if not content_file:
                raise ValueError("未提供代码片段文件路径")

            if not os.path.exists(content_file):
                self.logger.log_error(f"代码片段文件不存在，注入失败: {content_file}")
                return False

            self.logger.log_operation("代码注入", f"从文件读取: {content_file}")

            # 加载代码片段
            snippets = self._load_snippets(content_file)

            if not snippets:
                self.logger.log_warning("没有找到可用的代码片段")
                return False

            self.logger.log_operation("片段加载", f"加载了 {len(snippets)} 个代码片段")

            # 查找目标文件
            target_files = self._find_target_files(target_dirs)

            if not target_files:
                self.logger.log_error("代码注入失败: 没有找到目标文件")
                return False

            self.logger.log_operation("目标文件", f"找到 {len(target_files)} 个目标文件")

            # 注入代码
            self.injected_count = 0
            for file_path in target_files:
                if self._inject_to_file(file_path, snippets):
                    self.injected_count += 1

            self.logger.log_operation(
                "代码注入完成",
                f"成功注入 {self.injected_count}/{len(target_files)} 个文件"
            )

            return True

        except Exception as e:
            self.logger.log_error(f"代码注入失败: {e}")
            return False

    def inject_snippets(self, snippets: List[str], target_files: List[str]) -> bool:
        """
        直接注入代码片段到指定文件

        Args:
            snippets: 代码片段列表
            target_files: 目标文件列表

        Returns:
            是否成功
        """
        try:
            self.logger.log_operation(
                "代码注入",
                f"注入 {len(snippets)} 个片段到 {len(target_files)} 个文件"
            )

            self.injected_count = 0
            for file_path in target_files:
                if self._inject_to_file(file_path, snippets):
                    self.injected_count += 1

            self.logger.log_operation(
                "代码注入完成",
                f"成功注入 {self.injected_count}/{len(target_files)} 个文件"
            )

            return True

        except Exception as e:
            self.logger.log_error(f"代码注入失败: {e}")
            return False

    def _load_snippets(self, content_file: str) -> List[str]:
        """
        从文件加载代码片段

        文件格式：
        - 每个片段用 "---" 分隔
        - 或者每行一个简单片段

        Args:
            content_file: 代码片段文件路径

        Returns:
            代码片段列表
        """
        try:
            with open(content_file, 'r', encoding='utf-8') as f:
                content = f.read()

            # 尝试按分隔符分割
            if '---' in content:
                snippets = [s.strip() for s in content.split('---') if s.strip()]
            else:
                # 否则按行分割
                snippets = [line.strip() for line in content.split('\n') if line.strip()]

            return snippets

        except Exception as e:
            self.logger.log_error(f"加载代码片段失败: {e}")
            return []

    def _find_target_files(self, target_dirs: List[str]) -> List[str]:
        """
        查找目标文件

        Args:
            target_dirs: 目标目录列表

        Returns:
            文件路径列表
        """
        target_files = []

        # 如果没有指定目录，使用项目根目录
        if not target_dirs:
            target_dirs = [self.config.project_path]

        # 白名单目录过滤
        white_list = self.config.advanced_features.injection_white_list_folders

        for target_dir in target_dirs:
            # 检查目录是否存在
            if not os.path.exists(target_dir):
                self.logger.log_warning(f"目标目录不存在: {target_dir}")
                continue

            # 如果是文件，直接添加
            if os.path.isfile(target_dir):
                if self._should_inject_file(target_dir, white_list):
                    target_files.append(target_dir)
                continue

            # 遍历目录
            for root, dirs, files in os.walk(target_dir):
                # 跳过隐藏目录和构建目录
                dirs[:] = [d for d in dirs if not d.startswith('.')
                          and d not in ['build', 'dist', '__pycache__', 'node_modules', 'DummyCode']]

                for file in files:
                    file_path = os.path.join(root, file)
                    if self._should_inject_file(file_path, white_list):
                        target_files.append(file_path)

        return target_files

    def _should_inject_file(self, file_path: str, white_list: List[str]) -> bool:
        """
        判断文件是否应该注入

        Args:
            file_path: 文件路径
            white_list: 白名单目录列表

        Returns:
            是否应该注入
        """
        # 检查文件扩展名
        language = detect_language_for_file(file_path)
        if not language:
            return False

        # 检查白名单
        if white_list:
            # 如果有白名单，只处理白名单中的文件
            for white_dir in white_list:
                if white_dir in file_path:
                    return True
            return False

        return True

    def _inject_to_file(self, file_path: str, snippets: List[str]) -> bool:
        """
        向文件注入代码片段

        Args:
            file_path: 文件路径
            snippets: 代码片段列表

        Returns:
            是否成功
        """
        try:
            # 读取原文件内容
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # 检测语言，选择注入位置
            language = detect_language_for_file(file_path)

            # 随机选择一个片段
            snippet = random.choice(snippets)

            # 根据语言格式化片段
            formatted_snippet = self._format_snippet(snippet, language)

            # 查找合适的注入位置
            injection_pos = self._find_injection_position(content, language)

            if injection_pos is None:
                self.logger.log_warning(f"无法找到合适的注入位置: {file_path}")
                return False

            # 插入代码
            new_content = content[:injection_pos] + formatted_snippet + content[injection_pos:]

            # 写回文件
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(new_content)

            self.logger.log_file_processing(file_path, "已注入代码")
            return True

        except Exception as e:
            self.logger.log_warning(f"注入代码失败 {file_path}: {e}")
            return False

    def _format_snippet(self, snippet: str, language: str) -> str:
        """
        根据语言格式化代码片段

        Args:
            snippet: 原始代码片段
            language: 编程语言

        Returns:
            格式化后的代码片段
        """
        # 添加适当的缩进和注释
        if language in ['swift', 'objc', 'java', 'kotlin']:
            return f"\n    // Injected code\n    {snippet}\n"
        return f"\n{snippet}\n"

    def _find_injection_position(self, content: str, language: str) -> Optional[int]:
        """
        查找合适的注入位置

        策略：
        1. Swift/Kotlin/Java: 在类定义内部的末尾（closing brace之前）
        2. ObjC: 在 @implementation 的末尾

        Args:
            content: 文件内容
            language: 编程语言

        Returns:
            注入位置（字符索引），如果找不到返回 None
        """
        if language in ['swift', 'java', 'kotlin']:
            # 查找最后一个 }
            last_brace = content.rfind('}')
            if last_brace != -1:
                return last_brace
        elif language == 'objc':
            # 查找 @end 之前的安全注入位置
            end_pos = content.rfind('@end')
            if end_pos != -1:
                line_start = content.rfind('\n', 0, end_pos)
                if line_start != -1:
                    return line_start
                return end_pos

        # 如果找不到合适位置，返回文件末尾
        return len(content)

    def get_statistics(self) -> dict:
        """获取注入统计"""
        return {
            'injected_count': self.injected_count
        }
